package calao.client;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import calao.client.i18n.EventText;
import calao.client.i18n.Languages;
import calao.client.i18n.Text;
import calao.client.ui.Styles;
import calao.client.util.LongMap;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/** Calao desktop client.
 */
public class Desktop 
implements EntryPoint 
{
	
	static Desktop instance;

	public final Text text = (Text)GWT.create(Text.class);
	public final Languages languages = (Languages)GWT.create(Languages.class);
	public final Images images = (Images)GWT.create(Images.class);

	final Shortcut startShortcut = new Shortcut("calao.Start", text.doStart(), images.start());
	
	final Shortcut workspaceShortcut = new WorkspaceShortcut(this);
	
	final Shortcut[] baseShortcuts = { startShortcut, workspaceShortcut };

	int userAgent;

	SessionServiceAsync sessionService;
	MemberServiceAsync memberService;
	WorkspaceServiceAsync workspaceService;
	
	SessionInfo sessionInfo;
	DesktopPanel desktopPanel;

	Vector permList = new Vector();
	HashMap shortcuts = new HashMap();
	Vector listeners = new Vector();
	
	LongMap memberMap = new LongMap();

	public void onModuleLoad()
	{
		instance = this;
		log("Initializing desktop");
		Window.enableScrolling(false);
		parsePageProperties();
		userAgent = detectUserAgent();
		log("User agent: "+getUserAgent());
		// bind services
		sessionService = (SessionServiceAsync)bindService(
				GWT.create(SessionService.class), "calao.client.SessionService");
		memberService = (MemberServiceAsync)bindService(
				GWT.create(MemberService.class), "calao.client.MemberService");
		workspaceService = (WorkspaceServiceAsync)bindService(
				GWT.create(WorkspaceService.class), "calao.client.WorkspaceService");
		// TODO handle history token if any available from History.gscoetToken()
		log("Desktop ready");		
		// now check for a session
		checkSession();
	}

	void checkSession()
	{
		sessionService.getSessionInfo(newHandleSessionCallback());		
	}

	ServiceCallback newHandleSessionCallback()
	{
		return new ServiceCallback() {
			public void onReturn(Object result) {
				sessionInfo = result instanceof SessionInfo? (SessionInfo)result : null;
				if (sessionInfo!=null) handleLogin();
				else showLoginPanel();
			}
			public void onThrow(Throwable t) {
				sessionInfo = null;
				showLoginPanel();
			}
		};
	}

	private void handleLogin()
	{
		log("User has permissions "+toString(sessionInfo.getPermissions()));
		// clear desktop items
		shortcuts.clear();
		listeners.clear();
		permList.clear();
		// set title
		String title = sessionInfo.preferences.title;
		if (title==null) title = text.calaoDesktop();
		setTitle(title);
		// register core permissions 
		register(SessionService.ADMIN, text.admin(), null);
		register(MemberService.ADMIN_MEMBERS, text.adminMembers(), null);		
		register(WorkspaceService.ADMIN_WORKSPACES, text.adminWorkspaces(), null);
		// register base shortcuts
		for (int i=0; i<baseShortcuts.length; i++) register(baseShortcuts[i]);
		// clear start shortcut
		startShortcut.getMenuPanel().clear();
		// activate features
		activateFeatures();
		// create desktop panel
		desktopPanel = new DesktopPanel(Desktop.this, sessionInfo);
		// notify listeners
		for (int i=0; i<listeners.size(); i++) {
			((DesktopListener)listeners.get(i)).onDisplay(desktopPanel);
		}
		// show desktop panel
		RootPanel.get().add(desktopPanel);		
		// and eventually refresh member list
		refreshMemberMap();
	}
	
	static String toString(String[] strings)
	{
		String result = "{";
		for (int i=0; i<strings.length; i++) {
			if (i>0) result += ", ";
			result += strings[i];
		}
		return result + "}";
	}
	
	public void refresh()
	{
		// always defer refresh to avoid inconsistencies
		ServiceCallback.defer(new Command() {
			public void execute() {
				RootPanel.get().clear();
				checkSession();
			}			
		});
	}
	
	void activateFeatures()
	{
		Iterator iter = DesktopFeature.MAP.values().iterator();
		while (iter.hasNext()) ((DesktopFeature)iter.next()).active = false;
		final String[] features = sessionInfo.preferences.features;
		if (features!=null) {
			for (int i=0; i<features.length; i++) {
				DesktopFeature feature = DesktopFeature.getFeature(features[i]);
				if (feature==null) log(new Exception("Unknown feature: "+features[i]));
				else {
					try {
						feature.activate();
						feature.active = true;
						log("Activated feature "+feature.getName());
					}
					catch (Exception e) {
						log(e);
					}
				}
			}
		}
	}
		
	void showLoginPanel()
	{
		Window.open(".login", "_self", "");
	}

	void log(String msg)
	{
		GWT.log(msg, null);
	}

	void log(Throwable e)
	{
		GWT.log("Exception raised", e);
	}

	public static Desktop getDesktop()
	{
		return instance;
	}

	public Object bindService(Object service, String serviceName)
	{
		((ServiceDefTarget)service).setServiceEntryPoint(getServiceURL(serviceName));
		return service;
	}
	
	public String getServiceURL(String serviceName)
	{
		return GWT.getHostPageBaseURL()+".svc/"+serviceName;
	}

	public void setView(String text)
	{
		setView(new Label(text));
	}

	public void setView(Widget widget)
	{
		setView(widget, null);
	}

	public void setView(Widget widget, String title)
	{
		if (desktopPanel!=null) desktopPanel.setView(widget, title, false);
	}

	public void stackView(Widget widget)
	{
		stackView(widget, null);
	}

	public void stackView(Widget widget, String title)
	{
		if (desktopPanel!=null) desktopPanel.setView(widget, title, true);
	}
	
	public void popView()
	{
		if (desktopPanel!=null) desktopPanel.popView();
	}
	
	public void refreshView(Widget widget)
	{
		if (desktopPanel!=null) desktopPanel.refreshView(widget);
	}
	
	public void setGreenStatus(String text)
	{
		setStatus(text, Styles.GREEN);
	}
	
	public void setYellowStatus(String text)
	{
		setStatus(text, Styles.YELLOW);
	}

	public void setRedStatus(String text)
	{
		setStatus(text, Styles.RED);
	}
	
	public void clearStatus()
	{
		if (desktopPanel!=null && desktopPanel.view!=null) {
			desktopPanel.view.clearStatus();
		}
	}

	private void setStatus(String text, String style)
	{
		if (desktopPanel!=null && desktopPanel.view!=null) {
			Label status = new Label(text);
			status.addStyleName(style);
			desktopPanel.view.setStatus(status);
			desktopPanel.view.refresh();
		}		
	}
	
	int detectUserAgent()
	{
		String ua = getNativeUserAgent();
		log("User agent: "+ua);
		if ( ua.indexOf("opera") != -1 ) return UserAgent.OPERA;
		if ( ua.indexOf("webkit") != -1 ) return UserAgent.SAFARI;
		if ( ua.indexOf("msie 6.") != -1 ) return UserAgent.IE6;
		if ( ua.indexOf("msie 7.") != -1 ) return UserAgent.IE7;
		if ( ua.indexOf("gecko") != -1 ) return UserAgent.GECKO;
		return UserAgent.UNKNOWN;
	}

	static native String getNativeUserAgent() /*-{
		if (window.opera) return "opera";
        return navigator.userAgent.toLowerCase();
	}-*/;

	static native String getPageProperty(String name) /*-{
		var value = calaoProps[name];
		return value? value : null;
	}-*/;

	public static native void setTitle(String name) /*-{
	    $wnd.document.title = name;
    }-*/;

	static native void parsePageProperties() /*-{
		calaoProps = new Object();
		var metas = $wnd.document.getElementsByTagName('meta');
		for (var i = 0, n = metas.length; i < n; ++i) {
			var meta = metas[i], name = meta.getAttribute('name'), content;
			if (name == 'calao:property') {
				content = meta.getAttribute('content');
				if (content) {
					var value, eq = content.indexOf('=');
					if (eq >= 0) {
						name = content.substring(0, eq);
						value = content.substring(eq+1);
					} else {
						name = content;
						value = '';
					}
					calaoProps[name] = value;
				}
			}
		}
    }-*/;

	public int getUserAgent()
	{
		return userAgent;
	}

	void logout()
	{
		try {
			sessionService.disconnect(newHandleSessionCallback());
		}
		catch (Exception e) {
			sessionInfo = null;
			showLoginPanel();
		}
	}

	String getLanguageName(String lang) 
	{
		return languages.getString(lang);
	}

	String[] getAvailableLanguages()
	{
		return getDesktopProperty(DesktopProperty.LOCALES).split(",");
	}
	
	String getDesktopProperty(String name) 
	{
		return getPageProperty("desktop."+name);
	}

	/** Registers a shortcut.
	 */
	public void register(Shortcut shortcut)
	{
		shortcuts.put(shortcut.getName(), shortcut);
	}

	/** Registers a permission.
	 * 
	 * @param permission the permission to register.
	 * @param displayName a display name for the permission.
	 * @param description a description of the permission.
	 */
	public void register(Permission permission, String title, DesktopFeature feature)
	{
		permission.title = title;
		permission.feature = feature;
		permList.add(permission);
	}	

	public Shortcut[] listAvailableShortcuts()
	{
		Shortcut[] result = new Shortcut[shortcuts.size()];
		Iterator iter = shortcuts.values().iterator();
		int i = 0;
		while (iter.hasNext()) {
			result[i++] = (Shortcut)iter.next();
		}
		return result;
	}

	public Shortcut[] listActiveShortcuts()
	{
		String shortcutNames[] = sessionInfo.preferences.shortcuts;
		if (shortcutNames==null) return listAvailableShortcuts();
		Shortcut[] shortcuts = new Shortcut[shortcutNames.length];
		for (int i=0; i<shortcutNames.length; i++) {
			final String name = shortcutNames[i]; 
			Shortcut shortcut = getShortcut(shortcutNames[i]);
			if (shortcut==null) {
				log(new Exception("Unknown shortcut: "+name));
				shortcut = new Shortcut(name, "("+name+")", null);
			}
			shortcuts[i] = shortcut;
		}
		return shortcuts;
	}

	public Shortcut getShortcut(String name)
	{
		return (Shortcut)shortcuts.get(name);
	}
	
	public void showErrorBox(String title, String htmlText)
	{
		showErrorBox(title, htmlText, null);
	}
	
	public void showErrorBox(String title, String htmlText, Widget widget)
	{
		final DialogBox box = new DialogBox();
		box.setText(title);
		VerticalPanel panel = new VerticalPanel();
		Button button = new Button(text.ok());
		button.addClickListener(new ClickListener() {
			public void onClick(Widget sender) {
				box.hide();
			}
		});
		panel.add(new HTML(htmlText));
		if (widget!=null) panel.add(widget);
		panel.add(button);
		panel.setWidth("400px");
		box.setWidget(panel);
		box.center();
	}
	
	public EventText getApplicationEventText(String appName)
	{
		return null;
	}
	
	private void refreshMemberMap()
	{
		memberService.listMembers(new ServiceCallback() {
			public void onReturn(Object result) {
				MemberInfo[] members = (MemberInfo[])result;
				memberMap.clear();
				for (int i=0; i<members.length; i++) {
					memberMap.put(members[i].guid, members[i]);
				}
			}						
		});
	}
	
	public MemberInfo getMember(long guid)
	{
		return (MemberInfo)memberMap.get(guid);
	}
	
	public void addListener(DesktopListener listener)
	{
		listeners.add(listener);
	}
	
	public void remove(DesktopListener listener)
	{
		listeners.remove(listener);
	}
	
	public boolean has(Permission permission)
	{
		return sessionInfo!=null && sessionInfo.hasPermission(permission.name);
	}
	
	public SessionServiceAsync getSessionService()
	{
		return sessionService;
	}
	
	public long getConnectedMemberId()
	{
		return sessionInfo!=null? sessionInfo.guid : -1;
	}

}
